﻿// owner 蒋家伟

#include "ResourceManagerWidget.h"

// std
#include <algorithm>

// qt
#include <QTreeWidget>
#include <QVBoxLayout>
#include <QIcon>

// jsdk
#include "IPrefixTree.h"
#include "Dbg_Warn_Define.h"
#include "UIBaseUtils.h"

using namespace jsdk;

#define NAME_COLUMN     0
#define SIZE_COLUMN     1
#define RATE_COLUMN     2
#define DELE_COLUMN     3

OwnerPtr<jsdk::ResourceManagerWidget> jsdk::ResourceManagerWidget::create(std::vector<std::wstring>&& rootFilePaths, bool isTransparent, bool isOnlyDisplayDir)
{
    if (rootFilePaths.empty())
        return nullptr;

    OwnerPtr<IPrefixTree> opPrefixTree = IPrefixTree::create();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opPrefixTree, L"前缀树生成失败", L"蒋家伟", L"2023-10-23");
    // 现根据rootfile路径长度排序，防止出现重复包含的路径，过滤一遍，保留根路径。短的路径一定是长路径的根路径
    std::sort(rootFilePaths.begin(), rootFilePaths.end(), [](const std::wstring& left, const std::wstring& right)->bool {
        return left.size() < right.size();
    });

    std::wstring errorMsg;
    for (const auto& iter : rootFilePaths)
    {
        opPrefixTree->addPrefix(iter, errorMsg);
    }

    // 当前路径根据前缀树过滤后，保留下来的一定是非包含关系的根路径
    const std::vector<std::wstring>& rootPaths = opPrefixTree->getPrefixStr();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(!rootPaths.empty(), L"根节点个数不应该为零", L"蒋家伟", L"2023-10-23");

    return NEW_AS_OWNERPTR(jsdk::ResourceManagerWidget, rootPaths, isTransparent,  isOnlyDisplayDir);
}

void jsdk::ResourceManagerWidget::show()
{
    QWidget::show();
}

void jsdk::ResourceManagerWidget::refresh(std::vector<std::wstring>&& rootFilePaths)
{
    if (rootFilePaths.empty())
        return;

    OwnerPtr<IPrefixTree> opPrefixTree = IPrefixTree::create();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opPrefixTree, L"前缀树生成失败", L"蒋家伟", L"2023-10-23");
    // 现根据rootfile路径长度排序，防止出现重复包含的路径，过滤一遍，保留根路径。短的路径一定是长路径的根路径
    std::sort(rootFilePaths.begin(), rootFilePaths.end(), [](const std::wstring& left, const std::wstring& right)->bool {
        return left.size() < right.size();
    });

    std::wstring errorMsg;
    for (const auto& iter : rootFilePaths)
    {
        opPrefixTree->addPrefix(iter, errorMsg);
    }

    // 当前路径根据前缀树过滤后，保留下来的一定是非包含关系的根路径
    const std::vector<std::wstring>& rootPaths = opPrefixTree->getPrefixStr();
    DBG_WARN_AND_RETURN_VOID_UNLESS(!rootPaths.empty(), L"根节点个数不应该为零", L"蒋家伟", L"2023-10-23");

    m_pTreeWidget->clear();
    bool success = initTreeWidget(rootPaths);
    DBG_WARN_AND_RETURN_VOID_UNLESS(success, L"树形控件初始化失败", L"蒋家伟", L"2023-10-23");
    return;
}

void jsdk::ResourceManagerWidget::setReadOnly(bool readOnly)
{
    m_isOnlyRead = readOnly;
}

void jsdk::ResourceManagerWidget::setHideHeader(bool isHide)
{
    if (isHide)
    {
        m_pTreeWidget->header()->hide();
    }
    else
    {
        m_pTreeWidget->header()->show();
    }
}

jsdk::ResourceManagerWidget::ResourceManagerWidget(const std::vector<std::wstring>& rootFilePaths, bool isTransparent ,bool isOnlyDisplayDir):
    m_isOnlyDisplayDir(isOnlyDisplayDir)
{
    QVBoxLayout* pMainLayout = new QVBoxLayout;
    m_pTreeWidget = new QTreeWidget;

    if (isTransparent)
    {
        m_pTreeWidget->setStyleSheet("background-color:transparent");
    }

    {
        // 信号与槽与初始化
        bool success = initTreeWidget(rootFilePaths);
        DBG_WARN_AND_RETURN_VOID_UNLESS(success, L"树形控件初始化失败", L"蒋家伟", L"2023-10-23");
        success = connect(m_pTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(onItemClicked(QTreeWidgetItem *, int)));
        DBG_WARN_AND_RETURN_VOID_UNLESS(success, L"控件点击触发信号与槽连接失败", L"蒋家伟", L"2023-10-23");
    }

    pMainLayout->addWidget(m_pTreeWidget);
    this->setMinimumWidth(450);
    this->setLayout(pMainLayout);
}

bool jsdk::ResourceManagerWidget::initTreeWidget(const std::vector<std::wstring>& rootFilePaths)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(m_pTreeWidget, L"树形控件未初始化", L"蒋家伟", L"2023-10-23");

    {
        // 1、设置表头
        m_pTreeWidget->setColumnCount(4);
        QStringList headList;
        headList << u8"名称" << u8"大小" << u8"占比" << u8"删除";
        m_pTreeWidget->setHeaderLabels(headList);
        m_pTreeWidget->setColumnWidth(NAME_COLUMN, 200);
        m_pTreeWidget->setColumnWidth(SIZE_COLUMN, 80);
        m_pTreeWidget->setColumnWidth(RATE_COLUMN, 80);
        m_pTreeWidget->setColumnWidth(DELE_COLUMN, 50);
    }

    {
        // 2、根据文件夹下文件设置控件
        // 用于递归的生成控件的表达式
        std::function<qint64(QTreeWidgetItem&, const QDir&)> fun = [&fun, this](QTreeWidgetItem& parentTreeItem, const QDir& parentDir)-> qint64 {

            const QFileInfoList& fileInfoList = parentDir.entryInfoList(QDir::Dirs | QDir::Filter::NoDotAndDotDot | QDir::Files);
            qint64 parentSize = 0;
            for (const auto& fileInfo : fileInfoList)
            {
                QTreeWidgetItem* pTreeItem = nullptr;

                QString filePath = fileInfo.filePath();
                if (fileInfo.isDir())
                {
                    pTreeItem = new QTreeWidgetItem;
                    DBG_WARN_AND_CONTINUE_UNLESS(pTreeItem, L"树形节点创建失败", L"蒋家伟", L"2023-10-23");

                    QDir dir(filePath);
                    qint64 currentDirSize = fun(*pTreeItem, dir);
                    parentSize += currentDirSize;

                    // 文件夹
                    pTreeItem->setText(NAME_COLUMN, dir.dirName());                             // 文件夹名称
                    pTreeItem->setIcon(NAME_COLUMN, m_dirIcon);                                 // 图标
                    pTreeItem->setText(SIZE_COLUMN, UIBaseUtils::getSizeStr(currentDirSize));   // 大小
                    pTreeItem->setData(SIZE_COLUMN, Qt::UserRole, currentDirSize);              // 设置当前文件的实际大小
                    pTreeItem->setIcon(DELE_COLUMN, m_deleteIcon);                              // 删除按钮图标
                }
                else if (fileInfo.isFile())
                {
                    qint64 currentDirSize = fileInfo.size();
                    parentSize += currentDirSize;

                    if (m_isOnlyDisplayDir)
                    {
                        continue;
                    }

                    pTreeItem = new QTreeWidgetItem;
                    DBG_WARN_AND_CONTINUE_UNLESS(pTreeItem, L"树形节点创建失败", L"蒋家伟", L"2023-10-23");

                    // 文件
                    pTreeItem->setText(NAME_COLUMN, fileInfo.fileName());                        // 文件名称
                    pTreeItem->setIcon(NAME_COLUMN, m_fileIcon);                                 // 文件图标
                    pTreeItem->setText(SIZE_COLUMN, UIBaseUtils::getSizeStr(currentDirSize));    // 显示大小
                    pTreeItem->setData(SIZE_COLUMN, Qt::UserRole, currentDirSize);               // 设置当前文件的实际大小
                    pTreeItem->setIcon(DELE_COLUMN, m_deleteIcon);                               // 删除按钮图标
                }
                else
                {
                    DBG_WARN_AND_CONTINUE_UNLESS(false, L"暂只支持文件夹和文件", L"蒋家伟", L"2023-10-23");
                }

                if (pTreeItem)
                {
                    parentTreeItem.addChild(pTreeItem);
                }
            }

            return parentSize;
        };

        // 将所有文件夹的路径遍历
        for (const auto& iter : rootFilePaths)
        {
            QFileInfo fileInfo(QString::fromStdWString(iter));
            QTreeWidgetItem* pTopTreeItem = nullptr;
            QString filePath = fileInfo.filePath();

            if (fileInfo.isDir())
            {
                pTopTreeItem = new QTreeWidgetItem;
                DBG_WARN_AND_CONTINUE_UNLESS(pTopTreeItem, L"树形节点创建失败", L"蒋家伟", L"2023-10-23");

                QDir dir(filePath);
                // 文件夹
                qint64 currentDirSize = fun(*pTopTreeItem, dir);
                pTopTreeItem->setText(NAME_COLUMN, filePath);                                    // 文件夹名称
                pTopTreeItem->setIcon(NAME_COLUMN, m_dirIcon);                                   // 图标
                pTopTreeItem->setText(SIZE_COLUMN, UIBaseUtils::getSizeStr(currentDirSize));     // 大小
                pTopTreeItem->setData(SIZE_COLUMN, Qt::UserRole, currentDirSize);                // 设置当前文件的实际大小
                pTopTreeItem->setIcon(DELE_COLUMN, m_deleteIcon);                                // 删除按钮图标
            }
            else if (fileInfo.isFile())
            {
                qint64 currentDirSize = fileInfo.size();

                if (m_isOnlyDisplayDir)
                {
                    continue;
                }

                pTopTreeItem = new QTreeWidgetItem;
                DBG_WARN_AND_CONTINUE_UNLESS(pTopTreeItem, L"树形节点创建失败", L"蒋家伟", L"2023-10-23");

                // 文件
                pTopTreeItem->setText(NAME_COLUMN, filePath);                                    // 文件名称
                pTopTreeItem->setIcon(NAME_COLUMN, m_fileIcon);                                  // 文件图标
                pTopTreeItem->setText(SIZE_COLUMN, UIBaseUtils::getSizeStr(currentDirSize));     // 大小
                pTopTreeItem->setData(SIZE_COLUMN, Qt::UserRole, currentDirSize);                // 设置当前文件的实际大小
                pTopTreeItem->setIcon(DELE_COLUMN, m_deleteIcon);                                // 删除按钮图标
            }
            else
            {
                DBG_WARN_AND_CONTINUE_UNLESS(false, L"暂只支持文件夹和文件", L"蒋家伟", L"2023-10-23");
            }

            if (pTopTreeItem)
            {
                m_pTreeWidget->addTopLevelItem(pTopTreeItem);
            }
        }
    }


    {
        // 3、根据文件大小计算每个文件的占比
        // 再深度递归下计算内存占比
        for (int i = 0; i < m_pTreeWidget->topLevelItemCount(); i++)
        {
            QTreeWidgetItem* pItem = m_pTreeWidget->topLevelItem(i);
            DBG_WARN_AND_CONTINUE_UNLESS(pItem, L"顶层存在无效子节点", L"蒋家伟", L"2023-10-23");
            bool bOk = false;
            qint64 size = pItem->data(SIZE_COLUMN, Qt::UserRole).toLongLong(&bOk);
            if (!bOk)
                continue;
            updateNodeByteRateDisplay(*pItem, size);
        }
    }

    m_pTreeWidget->expandAll();
    return true;
}

void jsdk::ResourceManagerWidget::updateNodeByteRateDisplay(QTreeWidgetItem& parentTreeItem, const qint64& totalSize)
{
    bool bOk = false;
    qint64 size = parentTreeItem.data(SIZE_COLUMN, Qt::UserRole).toLongLong(&bOk);
    if (!bOk)
        return;
    double rate = static_cast<double>(static_cast<long double>(size) / totalSize * 100);
    parentTreeItem.setText(RATE_COLUMN, QString(u8"%1%").arg(rate, 0, 'f', 2));

    for (int i = 0; i < parentTreeItem.childCount(); i++)
    {
        QTreeWidgetItem* pItem = parentTreeItem.child(i);
        DBG_WARN_AND_CONTINUE_UNLESS(pItem, L"存在无效的子节点", L"蒋家伟", L"2023-10-23");
        updateNodeByteRateDisplay(*pItem, totalSize);
    }
}

void jsdk::ResourceManagerWidget::onItemClicked(QTreeWidgetItem * pItem, int column)
{
    if (pItem == nullptr)
        return;

    if (column != 3)
        return;
    // 等于3表示删除当前节点

    // 只读状态直接返回
    if (m_isOnlyRead)
        return;

    {
        // 1、先获取当前节点的完整路径然后删除当前完整路径下的所有文件
        QTreeWidgetItem* pTempItem = pItem;
        QString filePath;
        while (pTempItem)
        {
            filePath = pTempItem->text(0) + filePath;
            if (pTempItem = pTempItem->parent())
            {
                filePath = u8'/' + filePath;
            }
        }

        // 得到完整路径，然后删除
        QFileInfo fileInfo(filePath);
        if (fileInfo.isDir())
        {
            QDir dir(filePath);
            bool success = dir.removeRecursively();
            DBG_WARN_AND_RETURN_VOID_UNLESS(success, filePath.toStdWString() + L"删除失败", L"蒋家伟", L"2023-10-23");
        }
        else if (fileInfo.isFile())
        {
            bool success = QFile::remove(filePath);
            DBG_WARN_AND_RETURN_VOID_UNLESS(success, filePath.toStdWString() + L"删除失败", L"蒋家伟", L"2023-10-23");
        }
        else
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(false, L"当前点击的文件类型暂不支持,不应该出现在ui控件上，请查看初始化节点的地方是否修改支持了除文件与文件夹之外的节点", L"蒋家伟", L"2023-10-25");
        }

        DBG_WARN(L"成功删除文件，其路径为 : " + filePath.toStdWString(), L"蒋家伟", L"2023-10-25");
    }

    {
        // 2、更新余下控件的userdata，每个节点对应的byte数量，用于后面更新页面的字节数与占比
        // 更新UserData里面的数据，从当前节点的父节点开始，依次减去值
        qint64 byteSize = pItem->data(SIZE_COLUMN, Qt::UserRole).toLongLong();
        QTreeWidgetItem* pTempItem = pItem;
        while (pTempItem)
        {
            qint64 remainSize = pTempItem->data(1, Qt::UserRole).toLongLong() - byteSize;
            pTempItem->setData(SIZE_COLUMN, Qt::UserRole, remainSize);
            pTempItem->setText(SIZE_COLUMN, UIBaseUtils::getSizeStr(remainSize));

            QTreeWidgetItem* pParent = pTempItem->parent();
            if (pParent == nullptr)
            {
                // 只会从break退出，此时当前pTempItem为根节点
                break;
            }
            else
            {
                pTempItem = pParent;
            }
        }

        // 到这里说明是顶层
        // 刷新一次字节显示
        DBG_WARN_AND_RETURN_VOID_UNLESS(pTempItem, L"顶层节点无效", L"蒋家伟", L"2023-10-25");
        updateNodeByteRateDisplay(*pTempItem, pTempItem->data(SIZE_COLUMN, Qt::UserRole).toLongLong());
    }

    {
        // 3、删除ui控件的节点
        if (QTreeWidgetItem* pParentItem = pItem->parent())
        {
            // 如果任然存在父节点，直接删掉当前即可
            pParentItem->removeChild(pItem);
        }
        else
        {
            // 没有父节点，则表示为顶层的根
            int index = m_pTreeWidget->indexOfTopLevelItem(pItem);
            DBG_WARN_AND_RETURN_VOID_UNLESS(index >= 0, L"当前节点不是顶层节点，却没有父节点", L"蒋家伟", L"2023-10-23");
            m_pTreeWidget->takeTopLevelItem(index);
        }

        // 成功删除一个节点
        emit deleteNodeSuccess();
    }
}
